﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using AlfarEngine.Utilities.ShadowMapping;

namespace AlfarEngine.Components
{
    public class Light : AComponent
    {
        #region Variables

        static protected List<Light> sInstances = new List<Light>();
        static protected QuadRenderComponent sQuad = null;

        protected float mRange;
        protected float mBaseSize;
        protected Color mColor;
        protected Rectangle mAreaRectangle;
        protected RenderTarget2D mRenderTarget;
        protected Texture2D mShadowMap;

        protected bool mOn;

        internal ShadowmapResolver mResolver;

        #endregion

        //--------------------------------------------

        #region Accessors

        public float range { get { return mRange; } set { setRange(value); } }
        public Color color { get { return mColor; } set { mColor = value; } }
        public bool on { get { return mOn; } set { mOn = value; } }

        #endregion

        //--------------------------------------------

        #region Methods

        public Light(GameObject pOwner)
            : base(pOwner)
        {
            mRange = 0;
            mAreaRectangle = Rectangle.Empty;
            mRenderTarget = null;
            mResolver = null;

            mColor = Color.White;

            mOn = true;

            setRange(200.0f);

            sInstances.Add(this);
        }

        ~Light()
        {
            sInstances.Remove(this);
        }

        public override void Update(GameTime pTime)
        {
            if (mOwner.dirty)
            {//The owner is tagged as dirty we recompute the area

                ComputeArea();

            }
        }


        /// <summary>
        /// Render the shadowMap of all visible light
        /// </summary>
        static public void RenderShadows(GameTime pTime)
        {
            for (int i = 0; i < sInstances.Count; i++)
            {
                sInstances[i].RenderShadowMap(pTime);
            }
        }

        static public void DrawShadow()
        {
            Engine.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
            for (int i = 0; i < sInstances.Count; i++)
            {
                Engine.spriteBatch.Draw((Texture2D)sInstances[i].mRenderTarget,
                                        sInstances[i].mOwner.position - new Vector2(Engine.currentViewport.X, Engine.currentViewport.Y),
                                        null, sInstances[i].mColor, 0, new Vector2(sInstances[i].mBaseSize * 0.5f, sInstances[i].mBaseSize * 0.5f), 1.0f, SpriteEffects.None, 0);
            }
            Engine.spriteBatch.End();
        }

        //*****************

        protected virtual void setRange(float pRange)
        {
            mRange = pRange;
            ComputeArea();
            
            int baseSize = (int)mRange * 2;

            if (baseSize < 32)
            {
                baseSize = 32;
            }
            else if (baseSize < 64)
            {
                baseSize = 64;
            }
            else if (baseSize < 128)
            {
                baseSize = 128;
            }
            else if (baseSize < 256)
            {
                baseSize = 256;
            }
            else if (baseSize < 512)
            {
                baseSize = 512;
            }
            mBaseSize = baseSize;

            mRenderTarget = new RenderTarget2D(Engine.game.GraphicsDevice, baseSize, baseSize);

            mOwner.localBounds = new Rectangle((int)(-mBaseSize * 0.5f), (int)(-mBaseSize * 0.5f), (int)mBaseSize, (int)mBaseSize);

            if (sQuad == null)
            {
                sQuad = new QuadRenderComponent();
                sQuad.Load();
            }

            mResolver = new ShadowmapResolver(Engine.game.GraphicsDevice, sQuad, ShadowmapSize.Size256, ShadowmapSize.Size1024);
            mResolver.LoadContent();
        }

        protected virtual void ComputeArea()
        {
            mAreaRectangle = new Rectangle((int)(mOwner.position.X - (mBaseSize * 0.5f)), (int)(mOwner.position.Y - (mBaseSize * 0.5f)), (int)(mBaseSize), (int)(mBaseSize));
        }

        //Render the shadowMap of this light
        protected virtual void RenderShadowMap(GameTime pTime)
        {
            if (!mOwner.IsVisible())
                return;


            if (!mOn)
            {//we just clear the render target
                Engine.game.GraphicsDevice.SetRenderTarget(mRenderTarget);
                Engine.game.GraphicsDevice.Clear(Color.Transparent);
                Engine.game.GraphicsDevice.SetRenderTarget(null);
                return;
            }

            //Save the current viewport to set it back later
            Rectangle lCurrentViewport = Engine.currentViewport;
            Engine.currentViewport = mAreaRectangle;

            Engine.game.GraphicsDevice.SetRenderTarget(mRenderTarget);
            Engine.game.GraphicsDevice.Clear(Color.Transparent);

            Engine.spriteBatch.Begin();
            //Render the shadow caster layer (1)
            Engine.root[1].Draw(pTime, Color.Black);
            Engine.dynamicRoot.Draw(pTime, Color.Black);

            Engine.spriteBatch.End();

            Engine.game.GraphicsDevice.SetRenderTarget(null);

            mResolver.ResolveShadows(mRenderTarget, mRenderTarget, Vector2.Zero);

            Engine.currentViewport = lCurrentViewport;
        }

        #endregion
    }
}